package main

import (
	"fmt"
	"reflect"
)

type User1 struct {
	Id   int
	Name string
	Age  int
}

func main() {
	/*
		反射 API（函数）：
			1.从 实例 到 Value（实例的类型和值信息，是一个 struct）
				func ValueOf(i interface{}) Value

			2.从 实例 到 Type（只有类型信息，是一个接口类型）
				func TypeOf(i interface{}) Type

			3.从 Type 到 Value
				//1.New 返回的是一个 Value，该 Value 的 type 为 PtrTo(typ)，即 Value 的 Type 是指定 typ 的指针类型
				func New(typ Type) Value

				//2.Zero 返回的是一个 typ 类型的零值，注意返回的 Value 不能寻址，值不可改变
		  		func Zero(typ Type) Value

				//3.如果知道一个类型值的底层存放地址，则还有一个函数式可以依据 type 和该地址值恢复出 Value 的
				func NewAt(typ Type, p unsafe.Pointer) Value

			4.从 Value 到 Type
				(可以直接调用 Value 的方法，因为 Value 内部存放着到 Type 类型的指针)
				func (v Value) Type() Type

			5.从 Value 到 实例
				（Value 本身就包含类型和值信息，reflect提供了丰富的方法)

				//该方法最通用，用来将 Value 转换为空接口，该空接口内部存放着具体类型实例
				//可以使用接口类型查询去还原为具体的类型
				func (v Value) Interface() (i interface{})

				//Value 自身也提供丰富的方法，直接将 Value 转换为简单类型实例，如果类型不匹配，则直接引起 panic
				func (v Value) Bool() bool
				func (v Value) Float() float64
				func (v Value) Int() int64
				func (v Value) Uint() uint64

			6.从 Value 的指针 到 值
				//如果 v 类型是接口，则 Elem() 返回接口绑定的实例的 Value，如果 v 类型是指针，则返回指针值的 Value，否则引起 panic
				func (v Value) Elem() Value

				//如果 v 是指针，则返回指针值的 Value，否则返回 v 自身，该函数不会引起 panic
				func Indirect(v Value) Value

			7.Type 指针和值的相互转换
				(1) 指针类型Type 到 值类型Type
					//t 必须是 array, chan, map, ptr, slice，否则会引起 panic
					//Elem返回的是其内部元素的 Type
					t.Elem() Type

				(2) 值类型Type 到 指针类型Type
					//PtrTo 返回的是指向 t 的指针型 Type
					func PtrTo(t Type) Type

			8.Value 值的可修改性
				（实例对象传递给接口的是一个完全的值拷贝，
				 如果调用反射的方法 reflect.ValueOf() 传进去的是一个值类型变量，则获得的 Value 实际上是原对象的副本，这个Value无论如何也不能被修改。
				 如果穿进去的是一个指针，虽然接口内部转换的也是指针副本，但是通过指针还是可以访问到最原始的对象，使用此情况下 Value 是能够被修改的。）
				step1：通过 CanSet 判断是否能修改
					func (v Value) CanSet() bool

				step2：通过 Set 进行修改
					func (v Value) Set(x Value)
	*/
	//Value值的可修改性
	u := User1{Id: 1, Name: "喜羊羊", Age: 18}

	va := reflect.ValueOf(u)
	vb := reflect.ValueOf(&u)

	//值类型是可修改的
	fmt.Println(va.CanSet(), va.FieldByName("Name").CanSet()) //false false

	//指针类型是可修改的
	fmt.Println(vb.CanSet(), vb.Elem().FieldByName("Name").CanSet()) //false true

	fmt.Printf("%v\n", vb) //&{1 喜羊羊 18}
	name := "灰太狼"
	vc := reflect.ValueOf(name)

	//通过 Set 函数修改变量的值
	vb.Elem().FieldByName("Name").Set(vc)
	fmt.Printf("%v\n", vb) //&{1 灰太狼 18}
}
